package biolean;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.List;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;

import javax.swing.JPanel;
import javax.swing.JToolTip;


import biolean.commands.Command;
import biolean.files.BioFile;
import biolean.logic.Connection;
import biolean.logic.GraphicElement;
import biolean.logic.Project;

public class AppCanvas extends JPanel implements Runnable, Commons, KeyListener {
	private Dimension d;
	//private AppCanvas AppPanel = this;
	//private FilterForm Form;
	protected Project project = null;
	private ArrayList<Connection> connections = new ArrayList<Connection>();
	private GraphicElement activeElement;
	private Connection activeConnection;
	private GraphicElement connectElement;

	public boolean isDragExternalFile;
	public String dragExternalFile;
	public BioFile dragFile;
	private boolean isRightPressed;
	private boolean mPressedKeys[] = new boolean[256];
	
	private String outputFolderPath = System.getProperty("user.dir") + File.separator + OUTPUT_DIR;
	private Biolean mBiolean;

	private class DrawingThread extends Thread
	{
		private AppCanvas mCanvas;
		public DrawingThread(AppCanvas canvas)
		{
			mCanvas = canvas;
		}
		@Override
		public void run()
		{
			while (true)
			{
				mCanvas.repaint();
				try
				{
					Thread.sleep(100);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
	}
	
	public AppCanvas(Biolean biolean) {
		mBiolean = biolean;
		project = new Project(connections, this);
		addKeyListener(this);
		addMouseListener(new MAdapter());
		addMouseMotionListener(new MMAdapter());
		setFocusable(true);
		d = new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT);
		setBackground(Color.white);
		setDoubleBuffered(true);

		initOutputFolder();
		repaint();
		DrawingThread thread = new DrawingThread(this);
		thread.start();
		//setFocusable(false);
	}

	public BioFile createBioFile(String fileName)
	{
		BioFile result = null ; 
		Class<?> biofileCls = Biolean.fileRegister.findClassByExt(Utils.getExtenstion( fileName));
		if (biofileCls != null)
		{
			try{
				result = (BioFile)biofileCls.newInstance();
				result.initialize(100, 100, fileName); 
				project.add(result);
				repaint();
			} catch (InstantiationException e){
				e.printStackTrace();
			} catch (IllegalAccessException e){
				e.printStackTrace();
			}
		}
		else
			Biolean.PrintToLog("File" + fileName + " is not valid BioFile");
		return result;
	}
	
	private void initOutputFolder() {
		File theDir = new File(outputFolderPath);
		if (!theDir.exists()) {
			System.out.println(outputFolderPath);
			boolean result = theDir.mkdir();  
		}
	}
	
	//Set<String> mNewFilenames = new Hashse
	//public void 
	
	
	public void addNewCommand(Class<?> instanceClass, String cmdName, Point p) {
		try
		{
			Command cmd = (Command)instanceClass.newInstance();
			if (p == null)
				cmd.initialize(this.getWidth() - this.getWidth()/2, this.getHeight() - this.getHeight()/2 - 50, cmdName);
			else
				cmd.initialize((int)p.getX(), (int)p.getY(), cmdName);
			project.add(cmd);
			
			Class<?>[] allowedFiles = cmd.getAllowedFileTypes();
			
			for (Class<?> fileClass : allowedFiles) 
			{
				BioFile newBioFile = (BioFile)fileClass.newInstance();
				Calendar cal = Calendar.getInstance();
				String fileName = outputFolderPath + File.separator;
				fileName = project.generateFilename( newBioFile , fileName + "newFile");
				newBioFile.initialize( cmd.getX() , cmd.getY() + 100 , fileName);
				//newBioFile.setReady(false);
				project.add(newBioFile);				
				project.connect(cmd, newBioFile);
				newBioFile.setReady(false);
				Connection connect = new Connection(cmd, newBioFile);
				connections.add(connect);
			}	
		} catch (InstantiationException e1){
			e1.printStackTrace();
		} catch (IllegalAccessException e1){
			e1.printStackTrace();
		}	
		repaint();
	}
	
	public void AddEmptyFile(Class<?> instanceClass, String fileName)
	{
		try
		{
			BioFile newFile = (BioFile)instanceClass.newInstance();
			newFile.initialize(this.getWidth() - this.getWidth()/2, this.getHeight() - this.getHeight()/2 - 50, fileName);
			newFile.setReady(false);
			project.add(newFile);
			repaint();
		} catch (InstantiationException e){
			e.printStackTrace();
		} catch (IllegalAccessException e){
			e.printStackTrace();
		}
	}
	
	public void AddEmptyFile(Class<?> instanceClass, String fileName, Point p)
	{
		try
		{
			BioFile newFile = (BioFile)instanceClass.newInstance();
			String folder = outputFolderPath + File.separator;
			fileName = project.generateFilename( newFile , folder + fileName); 
			
			newFile.initialize((int)p.getX(),(int)p.getY(), fileName);
			newFile.setReady(false);
			project.add(newFile);
			repaint();
		} catch (InstantiationException e){
			e.printStackTrace();
		} catch (IllegalAccessException e){
			e.printStackTrace();
		}
	}

	public void paint(Graphics g) {
		super.paint(g);
		g.setColor(Color.white);
		// g.fillRect(CANVAS_OFFSET_X, CANVAS_OFFSET_Y, d.width, d.height);
		drawElements(g);
		drawConnections(g);
	}

	public void drawElements(Graphics g) {
		for (int i = 0; i < project.size(); i++) {
			GraphicElement element = /* (GraphicElement) */project.get(i);
			element.draw(g);
		}
	}

	public void drawConnections(Graphics g) {
		for (int i = 0; i < connections.size(); i++) {
			Connection connection = (Connection) connections.get(i);
			connection.draw(g);
		}
	}

	public GraphicElement getClickedElem(int xx, int yy) {
		GraphicElement result = null;
		for (int i = 0; i < project.size(); i++) {
			GraphicElement element = /* (GraphicElement) */project.get(i);
			if (element.isMouseOverElem(xx, yy, element)) {
				result = element;
			} else {
				element.setActive(false);
			}
		}
		return result;
	}

	public Connection getClickedConnection(int xx, int yy) {
		Connection result = null;
		for (int i = 0; i < connections.size(); i++) {
			Connection conn = (Connection) connections.get(i);
			if (conn.isMouseOverConnection(xx, yy, conn)) {
				result = conn;
			} else {
				conn.setActive(false);
			}
		}
		return result;
	}

	public void deleteConnection(List<GraphicElement> l, GraphicElement elem) {
		for (int i = 0; i < l.size(); i++) {
			GraphicElement element = l.get(i);
			for (int j = 0; j < connections.size(); j++) {
				Connection conn = (Connection) connections.get(j);
				if (((conn.outputElem == element) && (conn.inputElem == elem))
						|| ((conn.outputElem == elem) && (conn.inputElem == element))) {
					connections.remove(conn);
				}
			}
		}
	}

	@Override
	public void run() {
	}

	public class MAdapter extends MouseAdapter {

		public void mousePressed(MouseEvent e) 
		{
			requestFocus();
			if (activeConnection != null) {
				activeConnection.setActive(false);
			}
			if (e.getButton() == 1) {
				activeElement = getClickedElem(e.getX(), e.getY());
				if (activeElement != null) {
					if (mPressedKeys[17] ==  true && Utils.isCommand(activeElement) ) //ak je stlaceny control / ctrl
					{
						project.SelectFirst(activeElement);
						repaint();
						return;
						
					}
					activeElement.setActive(true);
					activeConnection = null;
				} else {
					activeConnection = getClickedConnection(e.getX(), e.getY());
					if (activeConnection != null) {
						activeConnection.setActive(true);
					}
				}
				if (e.getClickCount() == 2 && !e.isConsumed() && activeElement != null)
				{
					e.consume();
					activeElement.performDoubleClick();
					repaint();
				}
			} else if (e.getButton() == 3) {
				isRightPressed = true;
				if (activeElement != null) {
					connectElement = getClickedElem(e.getX(), e.getY());
					if ((connectElement != null)
							&& (activeElement != connectElement)
							&& project.connect(activeElement, connectElement)) {
						Connection connect = new Connection(activeElement,
								connectElement);
						connections.add(connect);
						connectElement = null;
						activeElement = null;
					}
				}
				activeElement = null;
			} else {
				activeElement = null;
			}
			repaint();
		}

		public void mouseReleased(MouseEvent e) {
			if (isRightPressed) {
				isRightPressed = false;
			}
			//mBiolean.requestFocus();
		}
	}

	private class MMAdapter extends MouseMotionAdapter {

		public void mouseDragged(MouseEvent e) {
			if ((activeElement != null) && !isRightPressed) {
				if (Utils.isCommand(activeElement) ) {
					activeElement.checkCommandXY(e.getX(), e.getY());
				} else if (Utils.isBioFile(activeElement) ) {
					activeElement.checkFileXY(e.getX(), e.getY());
				}
				repaint();
			}
		}

		public void mouseMoved(MouseEvent e) 
		{
			if (isDragExternalFile && dragFile != null) 
			{
				isDragExternalFile = false;
				//GraphicElement element = new BioFile();
				dragFile.initialize(e.getX(), e.getY(), dragExternalFile);
				dragFile.setReady(true);
				//project.add(dragFile);
				dragFile = null;
				repaint();
			}
		}
	}

	public void deleteElement( GraphicElement element)
	{
		List<GraphicElement> inputs = activeElement.getInputs();
		List<GraphicElement> outputs = activeElement.getNeighbours();
		deleteConnection(inputs, element);
		deleteConnection(outputs, element);
		project.remove(element);
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		if ( e.getKeyCode() < 256)
		{
			mPressedKeys[e.getKeyCode()] = true;
		}
		if (e.getKeyCode() == 127 && activeConnection != null) 
		{
			connections.remove(activeConnection);
			project.disconnect(activeConnection.outputElem, activeConnection.inputElem);
			repaint();
			activeConnection = null;
		} else if (e.getKeyCode() == 127 && activeElement != null) 
		{
			deleteElement( activeElement );
			repaint();
			activeElement = null;
		}else if (e.getKeyCode() == KeyEvent.VK_F5){
			mBiolean.requestFocus();
		}
	}

	@Override
	public void keyReleased(KeyEvent e) 
	{
		if ( e.getKeyCode() < 256)
		{
			mPressedKeys[ e.getKeyCode() ] = false;
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {

	}

}
